package rankUtil

import (
	"fmt"
	"sort"

	"7qule.com/sd3/CrossServer/src/common/systemConstant"
	"public.com/Framework/logMgr"
	"public.com/goutil/syncUtil"
)

//排序比较的方法
type ComparisonFunc = func(*BaseRankItem, *BaseRankItem) bool

//排名改变回调方法
type RankChangeCallbackFunc = func(item *BaseRankItem, beforeRank int32)

//快速排行榜工具
//     应用场景：
//         数据量大（10000以上）
//         实时排名
//         频繁改变排名值
//
//     使用方法：
//         1.数据加载完成后使用初始排序（InitSort），设置排名，记录上一项和下一项。100万数据耗时约0.5秒
//         2.排名值改变时更新排名（UpdateRank），排名数据改变越小，速度越快，数据量越大比完全排序节约的时间越多
//
// 作者：GAHD
// 日期：2020-04-27 14:15:07
type FastRankUtil struct {
	//排名Id字典，[RankId:]
	rankIdMap map[string]*BaseRankItem

	//排名字典，[Rank:]
	rankMap map[int32]*BaseRankItem

	//排行榜中第一项
	firstItem *BaseRankItem

	//排行榜中最后一项
	lastItem *BaseRankItem

	//读写锁
	dataLock *syncUtil.RWLocker

	//最大排名数量
	maxRankCount int32

	//排名改变回调方法
	rankChangeCallback RankChangeCallbackFunc

	//排名比较方法
	comparison ComparisonFunc
}

// 创建一个快速排行榜工具
// 参数：
//		maxRankCount：最大排名数量(小于或等于0表示不限制排名数量)
//		rankChangeCallbackFunc：排名改变回调方法
//		comparison：排名比较方法
// 返回：
//		快速排行榜工具
func NewFastRankUtil(maxRankCount int32, rankChangeCallbackFunc RankChangeCallbackFunc, comparison ComparisonFunc) *FastRankUtil {
	if comparison == nil {
		panic(fmt.Sprintf("NewFastRankUtil，排序比较方法不能为空！"))
	}

	return &FastRankUtil{
		maxRankCount:       maxRankCount,
		rankChangeCallback: rankChangeCallbackFunc,
		comparison:         comparison,
		rankIdMap:          make(map[string]*BaseRankItem, 8),
		rankMap:            make(map[int32]*BaseRankItem, 8),
		dataLock:           syncUtil.NewRWLocker(),
	}
}

//#region 排序处理

// 初始排序
// 参数：
// 		dataList:排序数据
// 		isNeedSort:是否需要排序，传false需要外部进行排序，如数据库里已经排序的情况
func (this *FastRankUtil) InitSort(dataList []*BaseRankItem, isNeedSort bool) {
	if dataList == nil {
		return
	}

	//根据自定义方法排序
	if isNeedSort {
		sort.Slice(dataList, func(i, j int) bool {
			return this.comparison(dataList[i], dataList[j])
		})
	}

	this.dataLock.Lock(systemConstant.DeathLockTime)
	defer this.dataLock.Unlock()

	//排名
	var rank int32 = 0

	//上一项
	var previousItem *BaseRankItem

	//记录上一项和下一项，以便进行快速排序
	for _, item := range dataList {
		//递增排名
		rank++

		//记录排名
		item.Rank = rank
		//记录上一项
		item.PreviousRankItem = previousItem

		//上一项不为空，则记录上一项的下一项
		if previousItem != nil {
			previousItem.NextRankItem = item
		}

		//记录当前项为上一项
		previousItem = item

		//记录排名项
		this.recordItem(item)

		//排名是否已满
		if this.isRankFull() {
			break
		}
	}
}

// 更新排名
// 参数：
//		item:排名项
//		changeType:排名值改变类型
// 返回：
//		排名是否改变
func (this *FastRankUtil) UpdateRank(item *BaseRankItem, changeType RankValueChangeType) bool {
	if item == nil {
		panic(fmt.Sprintf("FastRankUtil.UpdateRank，排名项不能为空！"))
	}

	this.dataLock.Lock(systemConstant.DeathLockTime)
	defer this.dataLock.Unlock()

	isChange := false

	//复制对象不能直接排名
	if item.IsCopy {
		tmp, exist := this.rankIdMap[item.RankId]
		if exist {
			tmp.Value1 = item.Value1
			tmp.Value2 = item.Value2
			tmp.ValueChangeTime = item.ValueChangeTime
			tmp.Object = item.Object
			item = tmp
		}
	}
	//用排名Id查找排名是否存在
	_, exists := this.rankIdMap[item.RankId]
	if !exists {
		//是否可加入排行榜
		if !this.isCanJoinRank(item) {
			return isChange
		}

		//新项的上一项为最后一项
		item.PreviousRankItem = this.lastItem
		//新项的无下一项
		item.NextRankItem = nil

		if this.lastItem != nil {
			//最后一项的下一项设置为新项
			this.lastItem.NextRankItem = item
			//设置新项的默认排名，默认最后一名
			item.Rank = this.lastItem.Rank + 1
		} else {
			item.Rank = 1
		}
	} else if changeType == RankValueChangeType_None {
		return isChange //未改变，不处理
	}

	var beforeRank = item.Rank

	//改变的值使排名靠前或是新项与上一项比较
	if changeType == RankValueChangeType_High || !exists {
		//循环更新排行榜，与上一项比较（使用递归会导致内存溢出）
		for this.updateRankToPrevious(item, this.comparison) {
			isChange = true
		}
	} else {
		//循环更新排行榜，与下一项比较
		for this.updateRankToNext(item, this.comparison) {
			isChange = true
		}
	}

	//记录排名项
	this.recordItem(item)

	//新加入了排名，检查移除多余的排名
	if !exists {
		this.removeMoreRank()
	}

	//通知排名改变
	this.raiseRankChange(item, beforeRank)

	return isChange
}

// 重置排行榜
func (this *FastRankUtil) ResetRank() {
	this.dataLock.Lock(systemConstant.DeathLockTime)
	defer this.dataLock.Unlock()

	//重置数据
	this.firstItem = nil
	this.lastItem = nil
	this.rankMap = make(map[int32]*BaseRankItem, 8)
	this.rankIdMap = make(map[string]*BaseRankItem, 8)
}

// 从排行榜中移除（已加锁）
// 参数：
// 		item:排名项
func (this *FastRankUtil) RemoveRank(item *BaseRankItem) {
	if item == nil {
		return
	}

	this.dataLock.Lock(systemConstant.DeathLockTime)
	defer this.dataLock.Unlock()

	this.removeRank(item)
}

// 从排行榜中移除（未加锁）
// 参数：
// 		item:排名项
func (this *FastRankUtil) removeRank(item *BaseRankItem) {
	if item == nil {
		return
	}

	var previousItem = item.PreviousRankItem
	var nextItem = item.NextRankItem

	//#region 衔接上下项

	if previousItem != nil {
		previousItem.NextRankItem = nextItem
	}

	if nextItem != nil {
		nextItem.PreviousRankItem = previousItem
	}

	//#endregion

	//#region 更新第一项和最后一项

	if item == this.firstItem {
		this.firstItem = nextItem
	}

	if item == this.lastItem {
		this.lastItem = previousItem
	}

	//#endregion

	//#region 处理删除项后面数据的排名

	//从删除项排名开始
	rank := item.Rank

	//从排名字典中移除
	delete(this.rankMap, item.Rank)

	//从排名Id字典中移除
	delete(this.rankIdMap, item.RankId)

	//没有下一项，则退出
	for nextItem != nil {
		//从排名字典中移除
		delete(this.rankMap, nextItem.Rank)

		//设置下一项排名
		nextItem.Rank = rank

		//记录到排名字典中
		this.rankMap[nextItem.Rank] = nextItem

		//更新下一项
		nextItem = nextItem.NextRankItem
		rank++
	}

	//#endregion
}

// 移除多余的排名
func (this *FastRankUtil) removeMoreRank() {
	if !this.isLimitRankCount() {
		return //不限制排名数量
	}

	//循环执行删除
	for this.lastItem != nil && this.lastItem.Rank > this.maxRankCount {
		this.removeRank(this.lastItem)
	}
}

// 是否可以加入排行榜
// 参数：
//		item：排名项
// 返回：
//		是否可以加入排行榜
func (this *FastRankUtil) isCanJoinRank(item *BaseRankItem) bool {
	//排名是否已满
	if !this.isRankFull() {
		return true
	}

	//直接跟最后一名比较
	return this.lastItem == nil || this.comparison(item, this.lastItem)
}

// 是否限制排名数量
// 返回：
//		是否限制排名数量
func (this *FastRankUtil) isLimitRankCount() bool {
	return this.maxRankCount > 0
}

// 排行榜是否已满
// 返回：
//		排行榜是否已满
func (this *FastRankUtil) isRankFull() bool {
	return this.isLimitRankCount() && this.lastItem != nil && this.lastItem.Rank >= this.maxRankCount
}

// 记录排名项
// 参数：
// 		item:排名项
func (this *FastRankUtil) recordItem(item *BaseRankItem) {
	//更新第一名
	if this.firstItem == nil || item.Rank < this.firstItem.Rank {
		this.firstItem = item
	}

	//更新最后一名
	if this.lastItem == nil || item.Rank > this.lastItem.Rank {
		this.lastItem = item
	}

	//记录到排名Id字典中
	this.rankIdMap[item.RankId] = item

	//记录到排名字典中
	this.rankMap[item.Rank] = item
}

// 更新排行榜与前一项比较
// 参数：
//		item:尝试更新排名的项
//		comparison:排序比较方法
// 返回：
//		是否改变排名
func (this *FastRankUtil) updateRankToPrevious(item *BaseRankItem, comparison ComparisonFunc) bool {
	previousItem := item.PreviousRankItem
	if previousItem == nil {
		return false //第一名
	}

	//跟前一名比较
	if !comparison(item, previousItem) {
		return false //未超过前一名
	}

	//记录之前的排名
	var beforeRank = previousItem.Rank

	//与上一项交换位置
	item.ExchangeWithPrevious()

	//记录排名项
	this.recordItem(previousItem)

	//通知排名改变
	this.raiseRankChange(previousItem, beforeRank)

	return true
}

// 更新排行榜与后一项比较
// 参数：
//		item:尝试更新排名的项
//		comparison:排序比较方法
// 返回：
//		是否改变排名
func (this *FastRankUtil) updateRankToNext(item *BaseRankItem, comparison ComparisonFunc) bool {
	var nextItem = item.NextRankItem
	if nextItem == nil {
		return false //最后一名
	}

	//跟下一名比较
	if !comparison(nextItem, item) {
		return false //未低于下一名
	}

	//记录之前的排名
	var beforeRank = nextItem.Rank

	//与下一项交换位置
	item.ExchangeWithNext()

	//记录排名项
	this.recordItem(nextItem)

	//通知排名改变
	this.raiseRankChange(nextItem, beforeRank)

	return true
}

// 通知排名改变
// 参数：
//		item:排名项
//		beforeRank:之前的排名
func (this *FastRankUtil) raiseRankChange(item *BaseRankItem, beforeRank int32) {
	if this.rankChangeCallback == nil {
		return
	}

	//避免出现错误
	defer func() {
		if r := recover(); r != nil {
			logMgr.UnknownErrorLog(r)
		}
	}()

	this.rankChangeCallback(item, beforeRank)
}

//#endregion

//#region 获取数据

// 获取当前排名数量
// 返回：
// 		当前排名数量
func (this *FastRankUtil) GetRankCount() int32 {
	this.dataLock.RLock(systemConstant.DeathLockTime)
	defer this.dataLock.RUnlock()

	if this.lastItem == nil {
		return 0
	}

	return this.lastItem.Rank
}

// 获取第一名
// 参数：
//		isCopy:是否返回副本
// 返回：
// 		第一名
func (this *FastRankUtil) GetFirstItem(isCopy bool) *BaseRankItem {
	this.dataLock.RLock(systemConstant.DeathLockTime)
	defer this.dataLock.RUnlock()

	if this.firstItem == nil {
		return nil
	}

	if isCopy {
		return this.firstItem.Clone()
	}

	return this.firstItem
}

// 获取最后一名
// 参数：
//		isCopy:是否返回副本
// 返回：
// 		最后一名
func (this *FastRankUtil) GetLastItem(isCopy bool) *BaseRankItem {
	this.dataLock.RLock(systemConstant.DeathLockTime)
	defer this.dataLock.RUnlock()

	if this.lastItem == nil {
		return nil
	}

	if isCopy {
		return this.lastItem.Clone()
	}

	return this.lastItem
}

// 通过排名Id获取排名项
// 参数：
//		rankId:排名项唯一Id
//		isCopy:是否返回副本
// 返回：
// 		排名项
// 		是否存在
func (this *FastRankUtil) GetRankItemById(rankId string, isCopy bool) (*BaseRankItem, bool) {
	this.dataLock.RLock(systemConstant.DeathLockTime)
	defer this.dataLock.RUnlock()

	item, exists := this.rankIdMap[rankId]
	if !exists {
		return nil, false
	}

	if isCopy {
		return item.Clone(), true
	}

	return item, true
}

// 通过排名获取排名项
// 参数：
//		rank:排名
//		isCopy:是否返回副本
// 返回：
// 		排名项
// 		是否存在
func (this *FastRankUtil) GetRankItemByRank(rank int32, isCopy bool) (*BaseRankItem, bool) {
	this.dataLock.RLock(systemConstant.DeathLockTime)
	defer this.dataLock.RUnlock()

	item, exists := this.rankMap[rank]
	if !exists {
		return nil, exists
	}

	if isCopy {
		return item.Clone(), exists
	}

	return item, exists
}

// 截取从第一名开始指定的数量的连续排名
// 参数：
//		count:截取数量
//		isCopy:是否返回副本
// 返回：
// 		排名列表
func (this *FastRankUtil) TakeRankListByFirst(count int32, isCopy bool) []*BaseRankItem {
	return this.TakeRankList(this.firstItem, count, true, isCopy)
}

// 获取跳过指定数量后指定数量的连续排名
// 参数：
//		skip:跳过数量
//		count:截取数量
//		isCopy:是否返回副本
// 返回：
// 		排名列表
func (this *FastRankUtil) TakeRankListBySkip(skip, count int32, isCopy bool) []*BaseRankItem {
	//获取开始的排名项
	item, exists := this.GetRankItemByRank(skip+1, false)
	if !exists {
		return nil
	}

	return this.TakeRankList(item, count, true, isCopy)
}

// 从指定排名开始截取指定的数量的连续排名
// 参数：
//		item:指定排名项
//		count:截取数量
//		isInclude:是否包含item
//		isCopy:是否返回副本
// 返回：
// 		排名列表
func (this *FastRankUtil) TakeRankList(item *BaseRankItem, count int32, isInclude, isCopy bool) []*BaseRankItem {
	if count <= 0 || item == nil {
		return nil
	}

	var result []*BaseRankItem

	this.dataLock.RLock(systemConstant.DeathLockTime)
	defer this.dataLock.RUnlock()

	//从指定项开始获取
	var nextItem *BaseRankItem
	if isInclude {
		nextItem = item
	} else {
		nextItem = item.NextRankItem
	}

	//获取指定数量的排名
	for i := int32(0); i < count; i++ {
		if nextItem == nil {
			break //到末尾
		}

		//添加到结果
		if isCopy {
			result = append(result, nextItem.Clone())
		} else {
			result = append(result, nextItem)
		}

		//切换到下一项
		nextItem = nextItem.NextRankItem
	}

	return result
}

// 获取排名列表
// 参数：
//		isCopy:是否返回副本
// 返回：
// 		排名列表
func (this *FastRankUtil) GetRankList(isCopy bool) []*BaseRankItem {
	this.dataLock.RLock(systemConstant.DeathLockTime)
	defer this.dataLock.RUnlock()

	if this.firstItem == nil || this.lastItem == nil {
		return nil
	}

	result := make([]*BaseRankItem, 0, this.lastItem.Rank)

	//从第一项开始获取
	currItem := this.firstItem

	//获取全部排名
	for currItem != nil {
		//添加到结果
		if isCopy {
			result = append(result, currItem.Clone())
		} else {
			result = append(result, currItem)
		}

		//切换到下一项
		currItem = currItem.NextRankItem
	}

	return result
}

//#endregion

//#region 组装数据

// 组装所有排名信息
// 参数：
//		fun：组装数据的方法
func (this *FastRankUtil) AssembleAllRankInfo(fun func(*BaseRankItem)) {
	//获取排行榜副本
	rankList := this.GetRankList(true)

	//组装完成后将复制的排名数据放回排名池中
	defer PutCopyRankItemToPool(rankList...)

	for _, item := range rankList {
		fun(item)
	}
}

// 组装指定数量的排名信息（从第一名开始）
// 参数：
//		rankCount：排名数量
//		fun：组装数据的方法
func (this *FastRankUtil) AssembleRankInfoByCount(rankCount int32, fun func(*BaseRankItem)) {
	//获取排行榜副本
	rankList := this.TakeRankListByFirst(rankCount, true)

	//组装完成后将复制的排名数据放回排名池中
	defer PutCopyRankItemToPool(rankList...)

	for _, item := range rankList {
		fun(item)
	}
}

//#endregion
